#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Description: 异常值、缺失值处理
# Created: dingwenjing 2023/3/28 15:56
from scipy.stats import iqr
import pandas as pd
import numpy as np


class OutlierMissing:
    @staticmethod
    def outliers(data, numeric_var, box_scale=3,is_replace=1):
        """箱形图法判断异常值,并进行相应处理

        :param data: pd数据
        :param numeric_var: 数值型变量
        :param box_scale: 尺度，一般为3
        :param is_replace: 1表示替换异常值 否则删除异常值所在行
        :return: pd数据
        """

        for i in numeric_var:
            iqr_val = iqr(data[i], nan_policy='omit')
            if iqr_val != 0:
                maxcut_stand = data[i].quantile(0.75) + iqr_val * box_scale
                mincut_stand = data[i].quantile(0.25) - iqr_val * box_scale
                if maxcut_stand != mincut_stand:
                    if is_replace == 1:
                        data.loc[data[i] >= maxcut_stand, i] = max(data.loc[data[i] < maxcut_stand, i])
                        data.loc[data[i] <= mincut_stand, i] = min(data.loc[data[i] > mincut_stand, i])
                    else:
                        data = data.drop(data.loc[data[i] >= maxcut_stand, i].index)
                        data = data.drop(data.loc[data[i] <= mincut_stand, i].index)
                        data.reset_index(drop=True, inplace=True)  # 行索引重排
                else:
                    threshold = 0.95  # 百分之九十五
                    for col in data.columns:
                        if (data[col].value_counts(normalize=True).max()) >= threshold:
                            data.drop(col, axis=1, inplace=True)



        return data

    @staticmethod
    def missings_numeric(data, numeric_var,is_fill=1):
        """连续型(Continuous) 变量缺失值补充

        :param data: pd数据
        :param numeric_var: 需处理的数值型变量列表
        :param is_fill: 1表示按平均值填充缺失值,其他表示删除缺失值所在的行
        :return: pd数据
        """

        if set(numeric_var)<set(data.columns.to_list()):
            #变量列表必须在数据列中
            #for i in numeric_var:
                #df.loc[df['money'].isnull(),'money']

                #data[ is.na(data[i]), i] < - mean(data[, i], na.rm = TRUE)
            pass
        else:
            pass


        return data

    @staticmethod
    def missings_cate(data, cate_var):
        """缺失值补充
         离散型(Discrete) 变数时用众数值(Mode)补充

        :param data: pd数据
        :param cate_var: 需处理的字符型变量列表
        :return: pd数据
        """

        if set(cate_var) < set(data.columns.to_list()):
            pass
        else:
            pass


        return data

    @staticmethod
    def remove_all_nan_columns(df):
        """
        删除所有值均为NaN的列
        :param df: 数据帧
        :return: 处理后的数据帧
        """
        # 获取所有值均为NaN的列
        all_nan_cols = df.columns[df.isna().all()].tolist()

        # 删除所有值均为NaN的列
        df = df.drop(columns=all_nan_cols)

        return df

    @staticmethod
    def remove_same_value_columns(df,input):
        """
        删除所有值都相同的列
        :param df: 数据帧
        :return: 处理后的数据帧
        """
        # 获取每列中不同的值的数量
        unique_counts = df.apply(pd.Series.nunique)

        # 获取所有值都相同的列
        same_value_cols = unique_counts[unique_counts == 1].index.tolist()
        if input.group_var.lower() in same_value_cols:
            same_value_cols.remove(input.group_var.lower())
        if input.time_type.lower() in same_value_cols:
            # print(input.time_type,same_value_cols)
            same_value_cols.remove(input.time_type.lower())
        if input.Y.lower()  in same_value_cols:
            same_value_cols.remove(input.Y.lower())
        if input.reel_key.lower()  in same_value_cols:
            same_value_cols.remove(input.reel_key.lower())
        # 删除所有值都相同的列
        df = df.drop(columns=same_value_cols)

        return df

    @staticmethod
    def remove_nan_percentage_columns(df, nan_threshold=0.05):
        """
        删除所有NaN值比例超过阈值的列
        :param df: 数据帧
        :param nan_threshold: NaN值比例的阈值
        :return: 处理后的数据帧
        """
        # 获取每列中NaN值的比例
        nan_percentages = df.isna().mean()

        # 获取NaN值比例超过阈值的列
        nan_percentage_cols = nan_percentages[nan_percentages > nan_threshold].index.tolist()

        # 删除NaN值比例超过阈值的列
        df = df.drop(columns=nan_percentage_cols)

        return df

    @staticmethod
    def remove_nan_percentage_rows(df, nan_threshold=0.05):
        """
        删除所有NaN值比例超过阈值的行
        :param df: 数据帧
        :param nan_threshold: NaN值比例的阈值
        :return: 处理后的数据帧
        """
        # 获取每行中NaN值的数量
        nan_counts = df.isna().sum(axis=1)

        # 获取NaN值比例超过阈值的行
        nan_percentage_rows = df.index[nan_counts / df.shape[1] > nan_threshold].tolist()

        # 删除NaN值比例超过阈值的行
        df = df.drop(index=nan_percentage_rows)

        return df

    @staticmethod
    def remove_missing_features(data, threshold):
        """
        # 示例用法
        # 假设您有一个名为"data"的DataFrame对象
        # 调用remove_missing_features函数，传入data和阈值0.6
        # 函数将删除缺失值比例超过0.6的特征，并返回更新后的DataFrame和已删除的特征列表
        """
        # 计算每个特征的缺失值比例
        missing_ratios = data.isnull().sum() / len(data)

        # 获取需要删除的特征
        features_to_remove = missing_ratios[missing_ratios > threshold].index.tolist()

        # 删除特征
        data = data.drop(features_to_remove, axis=1)

        return data, features_to_remove

    @staticmethod
    def fill_missing_values(data, cols, gp_col):
        # 查询nan的列
        df_na = data[cols].isna()

        # 根据分组计算平均值
        df_mean = data.groupby(gp_col)[cols].mean()

        # 依次处理每一列
        for col in cols:
            na_series = df_na[col]
            names = list(data.loc[na_series, gp_col])
            t = df_mean.loc[names, col]
            t.index = data.loc[na_series, col].index
            # 相同的index进行赋值
            data.loc[na_series, col] = t

        return data


    @staticmethod
    def fill_missing_with_mean(data):
        """
        # 示例用法
        # 假设您有一个名为"data"的DataFrame对象
        # 调用fill_missing_with_mean函数，传入data
        # 函数将使用每列的平均值填充缺失值，并返回填充后的DataFrame
        """
        # 计算每列的平均值
        mean_values = data.mean()

        # 使用平均值填充缺失值
        data_filled = data.fillna(mean_values)

        return data_filled


    @staticmethod
    def deal_with_outlier(df1, multiple=3):

        df1.fillna(df1.mean(), inplace=True)  # 直接以列均值填充空值
        if multiple is None:
            pass
        else:
            # df1 = df1.loc[~(np.abs(df1-df1.mean()) > 6 * df1.std()).any(axis=1)] # 删除 6倍标准差 的异常数据
            df1 = df1.loc[~(np.abs(df1 - df1.mean()) > multiple * df1.std()).any(axis=1)]  # 删除  multiple 倍标准差 的异常数据
        return df1


